package com.wulis.config.swagger;

import static com.wulis.common.constant.GlobalConstant.BASIC;
import static com.wulis.common.constant.GlobalConstant.SWAGGER_URL;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Base64;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint;
import org.springframework.stereotype.Component;

import com.wulis.config.security.AuthUserDetails;

import lombok.extern.slf4j.Slf4j;

/**
 * @author WuliBao
 * @date 2020-03-26
 */
@Slf4j
@Component
public class SwaggerSecurityConfig {
    
    @Value("${knife4j.username}")
    private String username;
    
    @Value("${knife4j.password}")
    private String password;
    
    @Value("${knife4j.production}")
    private Boolean production;
    
    private static final Base64.Decoder DECODER = Base64.getUrlDecoder();
    
    private static final String[] SWAGGER_ANT_MATCHERS =
        {"/swagger-resources/**", "/service-worker.js", "/webjars/**", "/v2/api-docs-ext"};
    
    /**
     * security
     *
     * @param httpSecurity httpSecurity
     * @throws Exception Exception
     */
    public void configure(HttpSecurity httpSecurity)
        throws Exception {
        if (!production) {
            httpSecurity
                // 过滤请求
                .authorizeRequests()
                // 放行swagger 静态资源
                .antMatchers(SWAGGER_ANT_MATCHERS)
                .permitAll();
        }
    }
    
    /**
     * swagger 开启basic认证
     *
     * @param httpServletRequest httpServletRequest
     */
    public void basicAuthentication(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse,
        AuthenticationException e)
        throws IOException {
        // swagger doc.html开启basic认证
        if (!production && httpServletRequest.getRequestURI().contains(SWAGGER_URL)) {
            BasicAuthenticationEntryPoint basicAuthenticationEntryPoint = new BasicAuthenticationEntryPoint();
            basicAuthenticationEntryPoint.commence(httpServletRequest, httpServletResponse, e);
        }
    }
    
    /**
     * JWT 过滤器
     *
     * @param authHeader authHeader
     * @param request request
     */
    public void doFilterInternal(String authHeader, HttpServletRequest request) {
        if (!production && authHeader != null && authHeader.startsWith(BASIC)) {
            try {
                // 判断basic 认证是否为swagger账号
                final String authToken =
                    new String(DECODER.decode(authHeader.substring(BASIC.length())), StandardCharsets.UTF_8);
                String basic = username + ":" + password;
                if ((basic).equals(authToken)) {
                    // 构建Swagger用户信息
                    AuthUserDetails userDetails = new AuthUserDetails();
                    userDetails.setAccountId(0L);
                    userDetails.getUserInfo().setUserName(username);
                    userDetails.setPassword(new BCryptPasswordEncoder().encode(password));
                    UsernamePasswordAuthenticationToken authentication =
                        new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
                    authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                    SecurityContextHolder.getContext().setAuthentication(authentication);
                }
            }
            catch (Exception e) {
                log.error(e.getMessage());
            }
        }
    }
    
}